/*
 * 版权所有 (C) 2015 知启蒙(ZHIQIM) 保留所有权利。[遇见知启蒙，邂逅框架梦]
 * 
 * https://zhiqim.org/project/zhiqim_framework/zhiqim_zml.htm
 *
 * Zhiqim Zml is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *          http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */
package org.zhiqim.zml;

import java.io.IOException;
import java.io.OutputStream;
import java.io.Writer;
import java.util.List;

import org.zhiqim.kernel.constants.SignConstants;
import org.zhiqim.kernel.extend.MapS;
import org.zhiqim.kernel.extend.MapSO;
import org.zhiqim.zml.exception.StatementException;
import org.zhiqim.zml.statement._Define;
import org.zhiqim.zml.statement._Function;
import org.zhiqim.zml.statement._Include;
import org.zhiqim.zml.statement._Return.ReturnException;

/**
 * ZhiqimML主类
 * 
 * @version v1.0.0 @author zouzhigang 2014-3-21 新建与整理
 */
public class Zml implements StatementNesting, SignConstants
{
    private ZmlEngine engine;                      //ZML引擎
    private Zml parent;                            //父ZML，在<#include>中有用
    
    // ZML三要素
    private String path;                            //ZML相对路径
    private long lastModified;                     //ZML最后修改时间
    private String content;                         //ZML内容
    
    private boolean isParsed;                      //ZML是否已解析，在缓存中则不需要再解析
    private List<Statement> stmtList;               //ZML语句列表
    private List<ZmlLineIndex> indexList;           //ZML行索引
    
    private long lastAccessed;                      //最后访问时间

    /**
     * 由业务创建ZML，加载当前内容生成ZML对象
     * 
     * @param content   ZML内容
     */
    public Zml(String content)
    {
        this(null, null, 0, content);
    }
    
    /**
     * 由业务创建ZML，需传入内容和ZML引擎
     * 
     * @param engine    ZML引擎
     * @param content   ZML内容
     */
    public Zml(ZmlEngine engine, String content)
    {
        this(engine, null, 0, content);
    }
    
    /**
     * 由ZmlLoader 加载ZML字符串进行处理
     * 
     * @param engine            ZML引擎
     * @param path              ZML相对路径
     * @param lastModified      ZML最后修改时间
     * @param content           ZML内容
     */
    public Zml(ZmlEngine engine, String path, long lastModified, String content)
    {
        this.engine = engine;
        this.path = path;
        this.lastModified = lastModified;
        this.content = content;
        this.lastAccessed = System.currentTimeMillis();
    }
    
    /** 获取ZML索引列表 */
    public List<ZmlLineIndex> getIndexList()
    {
        if (indexList != null)
            return indexList;
        
        synchronized (this)
        {
            if (indexList == null)
                indexList = StatementParser.getIndexList(content);
        }
        
        return indexList;
    }
    
    /** 解析ZML得到语句列表 */
    public void parse() throws StatementException
    {
        this.stmtList = StatementParser.parseStatementNesting(this);
        
        //解析完之后把内容置为NULL，因为这个占用内存比较大
        this.isParsed = true;
        this.content = null;
    }
    
    /**
     * 输出到返回结果中
     * 
     * @param variableMap   指定的变量表，如_Include中调用
     * @return              输出的内容
     * @throws Exception    异常
     */
    public void define(ZmlVariable variableMap) throws StatementException
    {
        if (!isParsed)
        {//没有解析，优先作解析，验证表达式和语句级是否有异常
            parse();
        }
        
        //把_Define的执行一遍
        for (Statement statement : stmtList)
        {
            if (statement instanceof _Define)
            {//执行预定义
                ((_Define)statement).define(variableMap);
            }
            else if (statement instanceof _Include)
            {//递归回调预定义
                ((_Include)statement).define(variableMap);
            }
        }
    }
    
    /**
     * 输出到返回结果中，在_Include中调用
     * 
     * @param variableMap   指定的变量表
     * @param parent        指定父ZML
     * @return              输出的内容
     * @throws Exception    异常
     */
    public String process(ZmlVariable variableMap, Zml parent) throws StatementException
    {
        this.parent = parent;
        return process(variableMap);
    }
    
    /**
     * 输出到字节流中，不会调用flush()
     * 
     * @param pageVariableMap       全页变量表
     * @param contextVariableMap    上下文变量表
     * @param out                   输出流
     * @param encoding              输出编码
     * @throws StatementException   可能产生的语句异常
     * @throws IOException          可能产生的IO异常
     */
    public void process(OutputStream out, String encoding, MapSO pageVariableMap, MapS... contextVariableMap) throws StatementException, IOException
    {
        ZmlVariable variableMap = new ZmlVariable();
        variableMap.setZml(this);
        variableMap.setVariableMap(pageVariableMap);
        variableMap.setContextMap(contextVariableMap);
        
        out.write(process(variableMap).getBytes(encoding));
    }
    
    /**
     * 输出到字符流中，不会调用flush()
     * 
     * @param pageVariableMap       全页变量表
     * @param contextVariableMap    上下文变量表
     * @param writer                字符流
     * @throws StatementException   可能产生的语句异常
     * @throws IOException          可能产生的IO异常
     */
    public void process(Writer writer, MapSO pageVariableMap, MapS... contextVariableMap) throws StatementException, IOException
    {
        ZmlVariable variableMap = new ZmlVariable();
        variableMap.setZml(this);
        variableMap.setVariableMap(pageVariableMap);
        variableMap.setContextMap(contextVariableMap);
        
        writer.write(process(variableMap));
    }
    
    
    /**
     * 输出到返回结果中
     * 
     * @param variableMap   指定的变量表，如_Include中调用
     * @return              输出的内容
     * @throws Exception    异常
     */
    private String process(ZmlVariable variableMap) throws StatementException
    {
        if (!isParsed)
        {//没有解析，优先作解析，验证表达式和语句级是否有异常
            parse();
        }
        
        //1.优先把_Def的执行一遍
        define(variableMap);
        
        //2.循环执行语句
        StringBuilder strb = new StringBuilder();
        try
        {
            StatementParser.processStatementList(stmtList, variableMap, strb);
            return strb.toString();
        }
        catch(ReturnException e)
        {
            return e.hasValue()?String.valueOf(e.getValue()):strb.toString();
        }
    }
    
    @Override
    public String getPrevStatement(Statement stmt) throws StatementException
    {
        List<Statement> statementList = getStatementList();
        int ind = statementList.indexOf(stmt);
        if (ind <= 0)
            return null;
        
        return statementList.get(ind-1).getStatement();
    }
    
    @Override
    public String getNextStatement(Statement stmt) throws StatementException
    {
        List<Statement> statementList = getStatementList();
        int ind = statementList.indexOf(stmt);
        if (ind == -1 || ind >= statementList.size()-1)
            return null;
        
        return statementList.get(ind+1).getStatement();
    }
    
    public ZmlEngine getEngine()
    {
        return engine;
    }
    
    public List<Statement> getStatementList() throws StatementException
    {
        if (!isParsed)
        {//没有解析，优先作解析，验证表达式和语句级是否有异常
            parse();
        }
        
        return stmtList;
    }
    
    public _Function getFunction(String name) throws StatementException
    {
        for (Statement stmt : getStatementList())
        {
            if (!(stmt instanceof _Function))
                continue;
            
            _Function function = (_Function)stmt;
            if (!function.getName().equals(name))
                continue;
            
            return function;
        }
        
        return null;
    }
    
    public boolean hasParent()
    {
        return parent != null;
    }
    
    public String getPath()
    {
        return path;
    }

    public long getLastModified()
    {
        return lastModified;
    }

    public long getLastAccessed()
    {
        return lastAccessed;
    }
    
    /** 获取ZML内容，当被解析之后内容将清空置为NULL */
    public String getContent()
    {
        return content;
    }
    
    /** 允许设置最后修改时间 */
    public void setLastModified(long lastModified)
    {
        this.lastModified = lastModified;
    }
    
    /** 允许设置内容 */
    public void setContent(String content)
    {
        this.content = content;
    }
    
    /** 重新设置最后访问时间 */
    public void setLastAccessed()
    {
        this.lastAccessed = System.currentTimeMillis();
    }
    
    /**************************************************************/
    //定义ZML内容从0开始，通过方向获取自身，适配Statement
    /**************************************************************/
    
    @Override
    public Zml getZml()
    {
        return this;
    }
    
    @Override
    public int getContentBeginIndex()
    {
        return 0;
    }
}
